/***********************************
 * 本文件用于实现socket编程，一步到位实现
 * 分别提供了client和server的socket准备工作的一次性接口
 ************************************/

#pragma once
#include <cerrno>
#include <cstring>

#include <iostream>

#include <sys/types.h>
#include <sys/socket.h>

#include "Log.hpp"
#include "Sockaddr.hpp"

namespace MySocket
{
    const static int defaultblock = 5;
    const static int buffer_size = 1024;

    // 基类(抽象类/接口类)
    class Socket
    {
    public:
        // 创建套接字
        virtual bool SocketInit() = 0;
        // bind
        virtual bool Bind() = 0;
        // accept
        virtual bool Accept() = 0;
        // listen
        virtual bool Listen() = 0;
        // server端支持主动关闭，导致绑定失败的问题
        virtual bool ReBind() = 0;
        // 拿到fd
        virtual int Getfd() = 0;
        // 拿listenfd
        virtual int GetListenfd() = 0;
        // 关闭fd
        virtual bool Closefd(int fd) = 0;
        // Server的Socket初始化
        bool ServerInit()
        {
            return SocketInit() && ReBind() && Bind() && Listen();
        }
    };

    // 派生类：用于实现TCP Server和Client 的套接字编程
    class TCP_Server_Socket : public Socket
    {
    public:
        // 服务端套接字
        TCP_Server_Socket(uint16_t port)
            : _sockaddr(port)
        {
        }

        // Server
        virtual bool SocketInit()
        {
            _listenfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_listenfd == -1)
            {
                _log.LogMessage(ClassFile, Error, "socket error, strerror : %s\n", strerror(errno));
                return false;
            }
            _log.LogMessage(ClassFile, Info, "socket success\n");
            return true;
        }
        // Server
        virtual bool Listen()
        {
            int ret = listen(_listenfd, defaultblock);
            if (ret == -1)
            {
                _log.LogMessage(ClassFile, Error, "listen error, strerror : %s\n", strerror(errno));
                return false;
            }
            _log.LogMessage(ClassFile, Info, "listen success\n");
            return true;
        }
        // Server
        virtual bool Bind()
        {
            int ret = bind(_listenfd, (sockaddr *)&_sockaddr.GetSockaddr(), _sockaddr.GetAddrlen());
            if (ret == -1)
            {
                _log.LogMessage(ClassFile, Error, "bind error, strerror : %s\n", strerror(errno));
                return false;
            }
            _log.LogMessage(ClassFile, Info, "bind success\n");
            return true;
        }
        // Server
        virtual bool ReBind()
        {
            int opt = 1;
            int ret = setsockopt(_listenfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
            if (ret == -1)
            {
                _log.LogMessage(ClassFile, Error, "setsockopt error, strerror : %s\n", strerror(errno));
                return false;
            }
            _log.LogMessage(ClassFile, Info, "setsockopt success\n");
            return true;
        }
        // Server
        virtual bool Accept()
        {
            sockaddr_in cilent_sockaddt_in;
            socklen_t cilent_sockaddt_in_addrlen;
            _sockfd = accept(_listenfd, (sockaddr *)&cilent_sockaddt_in, &cilent_sockaddt_in_addrlen);
            if (_sockfd == -1)
            {
                _log.LogMessage(ClassFile, Error, "accept error, strerror : %s\n", strerror(errno));
                return false;
            }
            _log.LogMessage(ClassFile, Info, "accept success\n");
            return true;
        }

        virtual int Getfd() { return _sockfd; }
        virtual int GetListenfd() { return _listenfd; }

        virtual bool Closefd(int fd)
        {
            int ret = close(fd);
            if (ret == -1)
            {
                _log.LogMessage(ClassFile, Error, "close error, strerror : %s\n", strerror(errno));
                return false;
            }
            _log.LogMessage(ClassFile, Info, "close success\n");
            return true;
        }

    private:
        int _sockfd;   // server 用于通信    的套接字
        int _listenfd; // server 用于listen 的套接字
        SockAddr _sockaddr;
    };
}